HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import type { Metadata } from 'next';2import Link from 'next/link';3import { notFound } from 'next/navigation';4import { orderedSections, QuietLine, TODAY_FEED_HREF, TODAY_LABELS, TodayItemRow } from '@/components/changes/today-sections';5import { DataStrip, SectionNav } from '@/components/layout/terminal';6import { BreadcrumbLd } from '@/components/meta/breadcrumb-ld';7import { ImportanceMark } from '@/components/ui/badges';8import { EntityLink } from '@/components/ui/entity';9import { Hint } from '@/components/ui/hint';10import { Container, Note, PageHeader, Section } from '@/components/ui/section';11import { EmptyState, Unavailable } from '@/components/ui/unavailable';12import { apiD3, safe } from '@/lib/api';13import { fmtDate, fmtInt, num } from '@/lib/format';14import { routes, SITE_NAME, SITE_URL } from '@/lib/site';1516type Params = { params: Promise<{ date: string }>; searchParams: Promise<{ include_backfill?: string }> };17const VALID = /^\d{4}-\d{2}-\d{2}$/;1819function shift(date: string, days: number): string {20 const d = new Date(`${date}T00:00:00Z`);21 d.setUTCDate(d.getUTCDate() + days);22 return d.toISOString().slice(0, 10);23}2425export async function generateMetadata({ params }: Params): Promise<Metadata> {26 const { date } = await params;27 if (!VALID.test(date)) return { title: 'Not found', robots: { index: false } };28 const title = `Today in AI — ${fmtDate(date)}`;29 const description = `What happened in AI on ${fmtDate(date)}: major releases, price moves, benchmark moves, model changes, research, open-weight releases, deprecations, provider and hardware changes — generated from the ${SITE_NAME} change log.`;30 return { title, description, alternates: { canonical: routes.changesDay(date) }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${routes.changesDay(date)}`, type: 'article' } };31}3233export default async function DailyPage({ params, searchParams }: Params) {34 const { date } = await params;35 const sp = await searchParams;36 if (!VALID.test(date) || Number.isNaN(new Date(`${date}T00:00:00Z`).getTime())) notFound();37 const today = new Date().toISOString().slice(0, 10);38 if (date > today) notFound();39 const backfill = sp.include_backfill === '1';40 const d = await safe(apiD3.changesDaily(date, 30, backfill));41 const { present, quiet } = orderedSections(d?.today);42 const total = num(d?.total) ?? (d ? Object.values(d.counts ?? {}).reduce<number>((n, v) => n + (num(v) ?? 0), 0) : 0);43 const excluded = num(d?.backfill_excluded) ?? 0;44 const prev = d?.previous_day ?? shift(date, -1);45 const next = d?.next_day && d.next_day <= today ? d.next_day : date < today ? shift(date, 1) : null;46 const strip = (d?.today ?? []).filter((s) => (num(s.total) ?? 0) > 0).slice(0, 8);4748 return (49 <Container wide>50 <BreadcrumbLd items={[{ name: SITE_NAME, href: '/' }, { name: 'Changes', href: '/changes' }, { name: fmtDate(date), href: routes.changesDay(date) }]} />51 <PageHeader52 eyebrow={53 <>54 <Link href={routes.changes()} className="hover:text-ink">55 Changes56 </Link>57 <span aria-hidden>/</span>58 <span>Today in AI</span>59 </>60 }61 title={62 <>63 What changed in AI on <span className="text-ink-2">{fmtDate(date)}</span>64 </>65 }66 lede={d ? <>{fmtInt(total)} events occurred on this UTC day{d.today ? ` across ${present.length} sections` : ''} — grouped when several documents describe one release, generated from the database, nothing editorial.</> : undefined}67 aside={68 <nav className="flex items-center gap-2 text-sm" aria-label="Day navigation">69 <Link href={routes.changesDay(prev)} className="inline-flex h-10 items-center border border-rule px-3 text-ink-2 hover:text-ink" rel="prev">70 ‹ {fmtDate(prev)}71 </Link>72 {next && (73 <Link href={routes.changesDay(next)} className="inline-flex h-10 items-center border border-rule px-3 text-ink-2 hover:text-ink" rel="next">74 {fmtDate(next)} ›75 </Link>76 )}77 {date !== today && (78 <Link href={routes.changesDay(today)} className="inline-flex h-10 items-center px-2 text-accent hover:underline">79 today80 </Link>81 )}82 </nav>83 }84 />85 <div className="pb-16">86 {!d ? (87 <Unavailable what="Daily digest" />88 ) : total === 0 && !d.new_models?.length && present.length === 0 ? (89 <EmptyState title="Nothing occurred on this day">90 {excluded > 0 ? (91 <>92 {fmtInt(excluded)} back-filled historical events fall on this date but are not “today” news — see them on the <Link href={`/timeline?year=${date.slice(0, 4)}&include_backfill=1`} className="link">timeline with backfill</Link> or <Link href={`${routes.changesDay(date)}?include_backfill=1`} className="link">include them here</Link>.93 </>94 ) : (95 'Connectors observed no material change occurring on this UTC day.'96 )}97 </EmptyState>98 ) : (99 <>100 {strip.length > 0 && <DataStrip dense items={strip.map((s) => ({ label: s.label ?? TODAY_LABELS[s.key] ?? s.key, value: fmtInt(s.total), href: `#${s.key.toLowerCase()}` }))} />}101 <p className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-ink-3">102 <span className="inline-flex items-center gap-1">103 Dates are when events <em>occurred</em>104 <Hint align="right" text="occurred_at = effective date when a source states one, else the observation time. Hover any date in the list for the observation time. `today` folds events sharing a group_key (one release seen in several documents) into one row." />105 </span>106 {excluded > 0 && (107 <span data-backfill-excluded>108 {fmtInt(excluded)} back-filled historical {excluded === 1 ? 'event' : 'events'} excluded ·{' '}109 <Link href={`/timeline?year=${date.slice(0, 4)}&include_backfill=1`} className="link">110 see them on the timeline111 </Link>{' '}112 ·{' '}113 <Link href={`${routes.changesDay(date)}${backfill ? '' : '?include_backfill=1'}`} className="link">114 {backfill ? 'hide backfill' : 'include here'}115 </Link>116 </span>117 )}118 {backfill && <span className="rounded-[3px] bg-surface-2 px-1.5 text-[10px] uppercase tracking-wide">including backfill</span>}119 </p>120 {present.length > 1 && <SectionNav items={present.map((s) => ({ id: s.key.toLowerCase(), label: s.label ?? TODAY_LABELS[s.key] ?? s.key }))} className="mt-3" />}121122 {d.new_models?.length > 0 && (123 <Section eyebrow="New models" title={<>{fmtInt(d.new_models.length)} new model{d.new_models.length === 1 ? '' : 's'}</>} hairline={false} action={{ href: `/models?year_from=${date.slice(0, 4)}&sort=release`, label: 'Models by release' }}>124 <ul className="grid gap-x-8 sm:grid-cols-2 lg:grid-cols-3">125 {d.new_models.slice(0, 30).map((m) => (126 <li key={m.id} className="flex items-baseline justify-between gap-3 border-b border-rule py-2 text-sm">127 <EntityLink e={m} className="font-medium" />128 <span className="shrink-0 text-xs text-ink-3">{m.organization?.name ?? ''}</span>129 </li>130 ))}131 </ul>132 {d.new_models.length > 30 && <Note className="mt-2">First 30 of {fmtInt(d.new_models.length)}.</Note>}133 </Section>134 )}135136 {present.map((s) => {137 const shown = s.items.length;138 const tot = num(s.total) ?? shown;139 const feed = TODAY_FEED_HREF[s.key]?.(date);140 return (141 <Section key={s.key} id={s.key.toLowerCase()} eyebrow={s.label ?? TODAY_LABELS[s.key] ?? s.key} title={<>{s.label ?? TODAY_LABELS[s.key] ?? s.key} <span className="tnum text-base font-normal text-ink-3">{fmtInt(tot)}</span></>} action={feed ? { href: feed, label: 'Feed' } : undefined}>142 <ul className="border-t border-rule">143 {s.items.map((it) => (144 <TodayItemRow key={it.id} it={it} />145 ))}146 </ul>147 {tot > shown && <Note className="mt-2">First {fmtInt(shown)} of {fmtInt(tot)}{feed ? <> — <Link href={feed} className="link">all in the feed →</Link></> : null}</Note>}148 {s.items.some((it) => it.importance >= 3) && (149 <p className="mt-2 flex items-center gap-1.5 text-[11px] text-ink-3">150 <ImportanceMark importance={3} /> major · <ImportanceMark importance={2} /> important · <ImportanceMark importance={1} /> notable151 </p>152 )}153 </Section>154 );155 })}156 <QuietLine quiet={quiet} labels={d.labels} />157 {d.note && <Note className="mt-4">{d.note}</Note>}158 </>159 )}160 </div>161 </Container>162 );163}164